mod kd;
mod file;

use std::{f64, fs, process, thread};
use std::fs::{File, OpenOptions};
use std::io::{self, BufRead, Write};
use std::path::Path;
use std::sync::{Arc, Mutex};
use indicatif::{ProgressBar, ProgressStyle};
use kdtree::KdTree;
use crate::file::get_file_rows;

#[derive(Debug, Clone, Copy)]
struct Point {
    x: f64,
    y: f64,

}

impl Point {
    fn new(x: f64, y: f64) -> Point {
        Point { x, y }
    }
    fn distance(&self, other: Point) -> f64 {
        const R: f64 = 6371.0; // 地球半径，单位为公里

        let lat1_rad = self.x.to_radians();
        let lon1_rad = self.y.to_radians();
        let lat2_rad = other.x.to_radians();
        let lon2_rad = other.y.to_radians();

        let dlat = lat2_rad - lat1_rad;
        let dlon = lon2_rad - lon1_rad;

        let a =
            (dlat / 2.0).sin().powi(2) + lat1_rad.cos() * lat2_rad.cos() * (dlon / 2.0).sin().powi(2);
        let c = 2.0 * a.sqrt().asin();

        R * c * 1000.0
    }
}

fn exit() -> ! {
    // 等待用户输入
    println!("Press Enter to exit...");
    let mut input = String::new();
    io::stdin().read_line(&mut input).unwrap();
    process::exit(1)
}

fn main() -> io::Result<()> {
    let mut all_position = get_file_rows("building.txt", "请在当前目录添加楼宇文件:building.txt\n数据行格式:\n经度,纬度,楼宇ID\n经度,纬度,楼宇ID")
        .unwrap_or_else(|message| {
            println!("{}", message);
            exit()
        });


    // 生成50万个随机坐标
    let points: Vec<(Point, String)> = all_position.into_iter().map(|item| {
        let array: Vec<&str> = item.split(',').collect();
        (Point {
            x: array[0].parse::<f64>().unwrap(),
            y: array[1].parse::<f64>().unwrap(),
        }, array[2].to_string())
    }).collect();

    // 构建KD树
    let mut kdtree = KdTree::new(2);
    for (point, id) in points {
        kdtree.add([point.x, point.y], (point, id)).unwrap();
    }
    let mut m_position = get_file_rows("merchant.txt", "请在当前目录添加商户文件:merchant.txt\n数据行格式:\n经度,纬度,商户ID\n经度,纬度,商户ID").unwrap_or_else(|message| {
        println!("{}", message);
        exit()
    });
    let tree_arc = Arc::new(kdtree);
    println!("开始离线计算数据,输出文件为:output.txt");
    // 文件路径
    let e_path = Path::new("output.txt");
    // 删除文件（如果文件存在）
    if e_path.exists() {
        fs::remove_file(e_path)?;
    }
    let out_file: File = OpenOptions::new()
        .create(true)
        .append(true)
        .open(e_path)?;
    // 打开文件，如果文件不存在则创建，否则追加数据
    let mut e_file = Arc::new(Mutex::new(out_file));
    // 创建一个进度条
    let pb = Arc::new(Mutex::new(ProgressBar::new(m_position.len() as u64)));

    // 设置进度条样式
    pb.lock().unwrap().set_style(
        ProgressStyle::default_bar()
            .template("{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {pos}/{len} ({eta})")
            .progress_chars("#>-"),
    );


    // 计算每个线程需要处理的元素数量
    let num_threads = 16;
    let chunk_size = (m_position.len() + num_threads - 1) / num_threads; // 向上取整
    let counter = Arc::new(Mutex::new(0));
    // 创建线程
    let mut handles = vec![];
    for i in 0..num_threads {
        let start = i * chunk_size;
        let end = (start + chunk_size).min(m_position.len());
        let array_slice = m_position[start..end].to_vec(); // 创建分片
        let tree_clone = Arc::clone(&tree_arc);
        let out_file_clone = e_file.clone();
        let thread_counter = Arc::clone(&counter);
        let arc_mutex_bar = Arc::clone(&pb);
        let handle = thread::spawn(move || {
            for m_line in array_slice {
                let m_parts: Vec<&str> = m_line.split(',').collect();
                if m_parts.len() > 2 {
                    let m_lat = m_parts[0].parse::<f64>().unwrap();
                    let m_lon = m_parts[1].parse::<f64>().unwrap();
                    let m_id = m_parts[2].to_string();
                    if let Ok(res) = tree_clone.nearest(&[m_lat, m_lon], 1, &|a, b| {
                        Point::new(a[0], a[1]).distance(Point::new(b[0], b[1]))
                    }) {
                        let (distance, (point, id)) = res[0];
                        // 获取互斥锁，确保在任何时刻只有一个线程可以访问计数器
                        let mut num = thread_counter.lock().unwrap();
                        // 增加计数器的值
                        *num += 1;
                        arc_mutex_bar.lock().unwrap().set_position(*num);
                        out_file_clone.lock().unwrap().write(format!("{},{},{},{},{},{},{:.2}/米\n", m_lat, m_lon, m_id, point.x, point.y, id, distance).as_bytes()).unwrap();
                    }
                }
            }
        });
        handles.push(handle);
    }

    // 等待所有线程完成
    for handle in handles {
        handle.join().unwrap();
    }

    // 完成进度条
    pb.lock().unwrap().finish_with_message("输出完成!");
    exit()
}

// 计算两个坐标之间的Haversine距离
fn haversine_distance(lat1: f64, lon1: f64, lat2: f64, lon2: f64) -> f64 {
    const R: f64 = 6371.0; // 地球半径，单位为公里

    let lat1_rad = lat1.to_radians();
    let lon1_rad = lon1.to_radians();
    let lat2_rad = lat2.to_radians();
    let lon2_rad = lon2.to_radians();

    let dlat = lat2_rad - lat1_rad;
    let dlon = lon2_rad - lon1_rad;

    let a =
        (dlat / 2.0).sin().powi(2) + lat1_rad.cos() * lat2_rad.cos() * (dlon / 2.0).sin().powi(2);
    let c = 2.0 * a.sqrt().asin();

    R * c * 1000.0
}
